#include "stdafx.h"

#include <MyFC/Glyph.h>

#include <vector>
#include <algorithm>

#include "mswin/winver.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

Glyph::Glyph()
	: m_Size(0, 0)
{
}

Glyph::~Glyph()
{
}

void Glyph::Load(const char * szGlyph, const char * szMask)
{
	if (m_DC.m_hDC != 0)
		VERIFY(m_DC.DeleteDC());

	if (m_Glyph.m_hObject != 0)
		VERIFY(m_Glyph.DeleteObject());
	VERIFY(m_Glyph.LoadBitmap(szGlyph));

	if (szMask != 0) {
		if (m_Mask.m_hObject != 0)
			VERIFY(m_Mask.DeleteObject());
		VERIFY(m_Mask.LoadBitmap(szMask));
	}

	VERIFY(m_DC.CreateCompatibleDC(0));
	VERIFY(m_DC.SelectObject(&m_Glyph));

	BITMAPINFO info;
	memset(&info, 0, sizeof(BITMAPINFOHEADER));
	info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	
	VERIFY(::GetDIBits(m_DC.m_hDC, (HBITMAP)m_Glyph.m_hObject, 0, 0, 0, &info, 0));

	m_Size.cx = info.bmiHeader.biWidth;
	m_Size.cy = info.bmiHeader.biHeight;

	//UpdateAlpha();
}

struct ReplaceGlyphAlpha {
	RGBQUAD operator () (RGBQUAD c, int m) const {
		if (m)
			c.rgbReserved = 255; //opaque
		else
			c.rgbReserved = 0; //transparent
		return c;
	};
};

void Glyph::UpdateAlpha()
{
	BITMAPINFO info;

	memset(&info, 0, sizeof(info.bmiHeader));	
	info.bmiHeader.biSize = sizeof(info.bmiHeader);
	info.bmiHeader.biBitCount = 32;
	info.bmiHeader.biPlanes = 1;
	info.bmiHeader.biWidth = m_Size.cx;
	info.bmiHeader.biHeight = m_Size.cy;

	std::vector<RGBQUAD> bits(m_Size.cx * m_Size.cy);
	GetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);

	std::vector<int> mask(m_Size.cx * m_Size.cy);
	if (m_Mask.m_hObject != 0)
		GetDIBits(m_DC.m_hDC, (HBITMAP) m_Mask.m_hObject, 0, m_Size.cy, &mask[0], &info, DIB_PAL_COLORS);
	else
		std::fill(mask.begin(), mask.end(), 1);

	std::transform(bits.begin(), bits.end(), mask.begin(), bits.begin(), ReplaceGlyphAlpha());

	SetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);
}

void Glyph::Draw(CDC * pDC, int x, int y, int align)
{
	CRect r = AlignRect(Bounds(), CPoint(x, y), align);
	DrawOverHere(pDC, r.left, r.top);
}

void Glyph::Draw(CDC * pDC, const CRect & rect, int align)
{
	CRect r = AlignRect(rect, Bounds(), align);
	DrawOverHere(pDC, r.left, r.top);
}

void Glyph::DrawOverHere(CDC * pDC, int x, int y)
{
	if (m_Mask.m_hObject != 0) {
		VERIFY(pDC->MaskBlt(x, y, m_Size.cx, m_Size.cy, &m_DC, 0, 0, m_Mask, 0, 0, MAKEROP4(SRCCOPY, SRCPAINT)));
	} else {
		if (mswin::is_winNT() && !mswin::is_win2000()) {
			VERIFY(pDC->BitBlt(x, y, m_Size.cx, m_Size.cy, &m_DC, 0, 0, SRCCOPY));
		} else {
			BLENDFUNCTION b = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
			VERIFY(AlphaBlend(pDC->m_hDC, x, y, m_Size.cx, m_Size.cy,
				m_DC.m_hDC, 0, 0, m_Size.cx, m_Size.cy, b));
		}
	}
}

// SysColorGlyph

void SysColorGlyph::Load(const char * szGlyph, const char * szMask)
{
	Glyph::Load(szGlyph, szMask);
	UpdateColors();
}

struct ReplaceGlyphSysColor 
{
	ReplaceGlyphSysColor(bool alpha = false) :
		m_Alpha(alpha) { }
	struct TableEntry {
		unsigned char value;
		COLORREF color;
	public:
		operator RGBQUAD() const {  
			RGBQUAD result;
			result.rgbRed = GetRValue(color);
			result.rgbGreen = GetGValue(color);
			result.rgbBlue = GetBValue(color);
			result.rgbReserved = 0;
			return result;
		}
	};
	bool IsGray(const RGBQUAD & c) const
		{ return c.rgbBlue == c.rgbGreen && c.rgbBlue == c.rgbRed; }
	void operator () (RGBQUAD & c) const {
		TableEntry table[] = {
			{ 0, RGB(0, 0, 0) },
			{ 128, GetSysColor(COLOR_BTNSHADOW) },
			{ 192, GetSysColor(COLOR_BTNFACE) },
			{ 255, GetSysColor(COLOR_BTNHILIGHT) }
		};
		unsigned int count = sizeof(table) / sizeof(TableEntry);
		if (!IsGray(c))
			return;
		unsigned char v = c.rgbBlue;
		unsigned char a = m_Alpha ? c.rgbReserved : 255;
		if (a != 0 && a != 255)
			v = v * 255 / a;
		for (unsigned int i = 1; i < count; ++i) {
			if (table[i - 1].value <= v && v <= table[i].value) {
				RGBQUAD c0 = table[i - 1];
				RGBQUAD c1 = table[i];
				int m = v - table[i - 1].value;
				int d = table[i].value - table[i - 1].value;
				//preserve alpha in c.rgbReserved!
				c.rgbBlue = a * (c0.rgbBlue + (BYTE) ((c1.rgbBlue - c0.rgbBlue) * m / d)) / 255;
				c.rgbGreen = a * (c0.rgbGreen + (BYTE) ((c1.rgbGreen - c0.rgbGreen) * m / d)) / 255;
				c.rgbRed = a * (c0.rgbRed + (BYTE) ((c1.rgbRed - c0.rgbRed) * m / d)) / 255;
				c.rgbReserved = a;
				return;
			}
		}
	};
private:
	bool	m_Alpha;
};

void SysColorGlyph::UpdateColors()
{
	BITMAPINFO info;
	memset(&info, 0, sizeof(info.bmiHeader));
	info.bmiHeader.biSize = sizeof(info.bmiHeader);

	VERIFY(GetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, 0, 0, &info, DIB_RGB_COLORS));
	bool alpha = (m_Mask.m_hObject == 0);

	memset(&info, 0, sizeof(info.bmiHeader));
	info.bmiHeader.biSize = sizeof(info.bmiHeader);
	info.bmiHeader.biBitCount = 32;
	info.bmiHeader.biPlanes = 1;
	info.bmiHeader.biWidth = m_Size.cx;
	info.bmiHeader.biHeight = m_Size.cy;

	std::vector<RGBQUAD> bits(m_Size.cx * m_Size.cy);
	GetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);

	//hack by Fedor 2006.05.20:
	//if the image has all the alphas equal to 0 (transparent), consider that it has no alpha channel at all
	if (alpha)
	{
		for (int i = 0; i != bits.size(); ++i)
			if (bits[i].rgbReserved != 0)
				break;
		alpha = i != bits.size();
	}

	std::for_each(bits.begin(), bits.end(), ReplaceGlyphSysColor(alpha));

	SetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);
}

// GrayedGlyph

void GrayedGlyph::Load(const char * szGlyph, const char * szMask)
{
	Glyph::Load(szGlyph, szMask);
	UpdateColors();
}

struct ReplaceGlyphGrayedColor {
	ReplaceGlyphGrayedColor(bool alpha = false) :
		m_Alpha(alpha) { }
	struct TableEntry {
		unsigned char value;
		COLORREF color;
	public:
		operator RGBQUAD() const {  
			RGBQUAD result;
			result.rgbRed = GetRValue(color);
			result.rgbGreen = GetGValue(color);
			result.rgbBlue = GetBValue(color);
			result.rgbReserved = 0;
			return result;
		}
	};
	RGBQUAD operator () (RGBQUAD c, short int m) const {
		TableEntry table[] = {
			{ 0, RGB(0, 0, 0) },
			{ 128, GetSysColor(COLOR_BTNSHADOW) },
			{ 192, GetSysColor(COLOR_BTNFACE) },
			{ 255, GetSysColor(COLOR_BTNHILIGHT) }
		};
		unsigned int count = sizeof(table) / sizeof(TableEntry);
		unsigned char v = int(c.rgbBlue + c.rgbGreen + c.rgbRed) / 3;
		unsigned char a = m_Alpha ? c.rgbReserved : 255;
		if (a != 0 && a != 255)
			v = v * 255 / a;
		if (m == 0)
			a = 0;
		v = 192 + (v - 192) / 3;
		for (unsigned int i = 1; i < count; ++i) {
			if (table[i - 1].value <= v && v <= table[i].value) {
				RGBQUAD c0 = table[i - 1];
				RGBQUAD c1 = table[i];
				int m = v - table[i - 1].value;
				int d = table[i].value - table[i - 1].value;
				//preserve alpha in c.rgbReserved!
				c.rgbBlue = a * (c0.rgbBlue + (BYTE) (c1.rgbBlue - c0.rgbBlue) * m / d) / 255;
				c.rgbGreen = a * (c0.rgbGreen + (BYTE) (c1.rgbGreen - c0.rgbGreen) * m / d) / 255;
				c.rgbRed = a * (c0.rgbRed + (BYTE) (c1.rgbRed - c0.rgbRed) * m / d) / 255;
				c.rgbReserved = a;
				return c;
			}
		}
		return c;
	};
private:
	bool	m_Alpha;
};

void GrayedGlyph::UpdateColors()
{
	BITMAPINFO info;

	memset(&info, 0, sizeof(info.bmiHeader));	
	info.bmiHeader.biSize = sizeof(info.bmiHeader);
	info.bmiHeader.biBitCount = 32;
	info.bmiHeader.biPlanes = 1;
	info.bmiHeader.biWidth = m_Size.cx;
	info.bmiHeader.biHeight = m_Size.cy;

	std::vector<RGBQUAD> bits(m_Size.cx * m_Size.cy);
	GetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);

	std::vector<int> mask(m_Size.cx * m_Size.cy);
	if (m_Mask.m_hObject != 0)
		GetDIBits(m_DC.m_hDC, (HBITMAP) m_Mask.m_hObject, 0, m_Size.cy, &mask[0], &info, DIB_PAL_COLORS);
	else
		std::fill(mask.begin(), mask.end(), 1);

	std::transform(bits.begin(), bits.end(), mask.begin(), bits.begin(), ReplaceGlyphGrayedColor(m_Mask.m_hObject == 0));

	SetDIBits(m_DC.m_hDC, (HBITMAP) m_Glyph.m_hObject, 0, m_Size.cy, &bits[0], &info, DIB_RGB_COLORS);
}

} // My
